/* GUS.C
 * Gravis UltraSound routines. Has never worked properly,
 * included only because mixer uses some variables/defines.
 *************
 * 29.08.2000 : Toni R„s„nen
 *              modifications/documentation for open source
 *
 */
//
// GUS.C
// Gravis UltraSound routines for DJGPP
//
// !!!!!!!!!!!! DOES NOT WORK PROPERLY! DO NOT ATTEMPT TO USE!
//
//

#include <stdio.h>
#include <dpmi.h>
#include <go32.h>
#include <pc.h>
#include "gus.h"

/*
#define MIDI_CONTROL           0x100
#define MIDI_DATA_REG          0x101
#define VOICE_REG              0x102
#define DATA_LO_REG            0x104
#define DATA_HI_REG            0x105
#define IRQ_STATUS_REG         0x006
#define TIMER_CONTROL_REG      0x008
#define TIMER_DATA_REG         0x009
#define DRAM_IO_REG            0x107
#define MIX_CTRL_REG           0x000
#define IRQ_CTRL_REG           0x00b
#define SPC_SELECT_REG         0x103
#define SPC_DATA_REG           0x107
#define SPC_DMA_CTRL            0x41
#define SPC_DMA_START_ADX       0x42
#define SPC_DRAM_IO_ADX_LO      0x43
#define SPC_DRAM_IO_ADX_HI      0x44
#define SPC_TIMER_CTRL          0x45
#define SPC_TIMER_1_COUNT       0x46
#define SPC_TIMER_2_COUNT       0x47
#define SPC_SAMPLE_FREQ         0x48
#define SPC_SAMPLE_CTRL         0x49
#define SPC_JOYSTIC_TRIM        0x4B
#define SPC_RESET               0x4C

  // voicecontrols: write=given value, read=given+80h
#define VCE_CTRL                0x00
#define VCE_FREQ_CONTROL        0x01
#define VCE_START_ADX_HI        0x02
#define VCE_START_ADX_LO        0x03
#define VCE_END_ADX_HI          0x04
#define VCE_END_ADX_LO          0x05
#define VCE_VOL_RAMP_RATE       0x06
#define VCE_VOL_RAMP_START      0x07
#define VCE_VOL_RAMP_END        0x08
#define VCE_VOLUME              0x09
#define VCE_CURR_ADX_HI         0x0A
#define VCE_CURR_ADX_LO         0x0B
#define VCE_BALANCE             0x0C
#define VCE_VOL_CTRL            0x0D
#define VCE_ACT_VOICES          0x0E
#define VCE_IRQ_STATUS          0x0F
*/

#define asmCLI __asm__ ("cli")
#define asmSTI __asm__ ("sti")

unsigned char GUSchanvol[32];
unsigned short gus_divisor;
unsigned short gus_baseadx;
unsigned short gus_select;
unsigned short gus_hidata;
unsigned short gus_lodata;
unsigned char gus_mix;
unsigned char gus_maxvoices;
unsigned char gus_irq,gus_midiirq,gus_dma,gus_adcdma;
unsigned short gus_adx;
unsigned char gus_oldintctrl1,gus_oldintctrl2;
unsigned char gus_intctrl1,gus_intctrl2;
unsigned char gus_volume;
unsigned char timerctrl=0,timermask=0;
unsigned char IntFromVoice;

unsigned char GUS_irqmasks[8] = { 1,2,4,8,16,32,64,128 };
unsigned char GUS_irqlatches[16] = {0,0,1,3,0,2,0,4,0,0,0,5,6,0,0,7};
unsigned char GUS_dmalatches[8] = {0,1,0,2,0,3,4,5};
unsigned short GUS_divisortable[19] = {44100,41160,38587,36317,34300,32494,30870,29400,28063,26843,
                              25725,24696,23746,22866,22050,21289,20580,19916,19293};
unsigned short GUS_linearvolume[65] = {0000,1796,2162,2376,2528,2646,2742,2823,2894,2956,3012,3062,3108,3150,3189,3226,3260,
                              3292,3322,3351,3378,3403,3428,3451,3474,3495,3516,3536,3555,3574,3592,3609,3626,3642,
                              3658,3673,3688,3702,3717,3730,3744,3757,3769,3782,3794,3806,3817,3829,3840,3851,3861,
                              3872,3882,3892,3902,3912,3921,3931,3940,3949,3958,3966,3975,3983,3992};

PFV girq_dma,
    girq_miditx,
    girq_midirx,
    girq_timer1,
    girq_timer2,
    girq_wave,
    girq_envelope;
_go32_dpmi_seginfo GUS_oldint,GUS_oldmidiint,info;
int gus_intvect,gus_midiintvect;

//*******************************************************************
unsigned short gus_adx_high (int adx)
{
  return ( (adx >> 7) & 0x1fff );
}

//*******************************************************************
unsigned short gus_adx_low (int adx)
{
  return ( (adx & 0x7f) << 9 );
}

//*******************************************************************
void GRAVIS_MIDI_INTERRUPT(void)
{
  //void
}

//*******************************************************************
void GRAVIS_INTERRUPT(void)
{ char count,irqsrc,ir,vce_ignore;
  count = 0;
  vce_ignore = 127;
  irqsrc = inportb(gus_baseadx+IRQ_STATUS_REG);
  while ((count < 100) && (irqsrc != 0))
    { //-------------------------------WAVE & ENVELOPE
      if (irqsrc & 0x60)
        { outportb (gus_select, VCE_IRQ_STATUS | 0x80);
          IntFromVoice = inportb (gus_hidata) & 0x1f;
          if ( (irqsrc & 0x20) && (vce_ignore != IntFromVoice) )
            { vce_ignore = IntFromVoice;
              girq_wave();
              outportb (gus_baseadx+VOICE_REG, IntFromVoice);
              outportb (gus_select, VCE_CTRL | 0x80);
              inportb (gus_hidata);
            }
          if ( (irqsrc & 0x40) && (vce_ignore != IntFromVoice) )
            { vce_ignore = IntFromVoice;
              girq_envelope();
              outportb (gus_baseadx+VOICE_REG, IntFromVoice);
              outportb (gus_select,VCE_CTRL | 0x80);
              inportb (gus_hidata);
            }
        }

      //-------------------------------DMA CONTROLLER
      if (irqsrc & 0x80)
        { outportb (gus_select,SPC_DMA_CTRL);
          inportb (gus_hidata);
          outportb (gus_select,SPC_DMA_CTRL);
          outportb (gus_hidata, 0x00);
        }

      //-------------------------------MIDI TX/RX
      if (irqsrc & 0x03)
        { ir = inportb(gus_baseadx+MIDI_CONTROL);
          if (ir & 1 == 1)
            { inportb(gus_baseadx+MIDI_DATA_REG);
              girq_miditx();
              outportb (gus_select, SPC_SAMPLE_CTRL);
              inportb (gus_hidata);
            }
          else
            { inportb(gus_baseadx+MIDI_DATA_REG);
              girq_midirx();
              outportb (gus_select, SPC_SAMPLE_CTRL);
              inportb (gus_hidata);
            }
        }
      //-------------------------------TIMER 1
      if (irqsrc & 0x04)
        { outportb (gus_select, SPC_TIMER_CTRL);
          outportb (gus_hidata, (timerctrl & ~0x04) );
          outportb (gus_select, SPC_TIMER_CTRL);
          outportb (gus_hidata, timerctrl);
          girq_timer1();
        }

      //-------------------------------TIMER 2
      if (irqsrc & 0x08)
        { outportb (gus_select, SPC_TIMER_CTRL);
          outportb (gus_hidata, (timerctrl & ~0x08) );
          outportb (gus_select, SPC_TIMER_CTRL);
          outportb (gus_hidata, timerctrl);
          girq_timer2();
        }


      count++;

      outportb (gus_select, SPC_SAMPLE_CTRL);
      inportb (gus_hidata);

      irqsrc = inportb(gus_baseadx+IRQ_STATUS_REG);
    }

  outportb (0x21, gus_intctrl1);
  outportb (0xa1, gus_intctrl2);
  outportb (0x20, 0x20);
  if (gus_irq > 7) { outportb (0xa0, 0x20); }
}

//*******************************************************************
char gus_peekdata(int adx)
{
  outportb (gus_baseadx+SPC_SELECT_REG, SPC_DRAM_IO_ADX_LO);
  outportw (gus_baseadx+DATA_LO_REG, (adx & 0xffff) );
  outportb (gus_baseadx+SPC_SELECT_REG, SPC_DRAM_IO_ADX_HI);
  outportb (gus_baseadx+DATA_HI_REG, (adx >> 16) & 0xff);
  return (inportb(gus_baseadx+DRAM_IO_REG));
}

//*******************************************************************
void gus_starttimer (char timer, char time)
{ char al;
  if (timer == 1)
    { timerctrl |= 4;
      timermask |= 1;
      al = 0x46;
    }
  else
    {
      timerctrl |= 8;
      timermask |= 2;
      al = 0x47;
    }
  outportb (gus_select, al);
  outportb (gus_hidata, 256-time);
  outportb (gus_select, SPC_TIMER_CTRL);
  outportb (gus_hidata, timerctrl);
  outportb (gus_baseadx+TIMER_CONTROL_REG, 0x04);
  outportb (gus_baseadx+TIMER_DATA_REG, timermask);
}

//*******************************************************************
void gus_stoptimer(char timer)
{ asmCLI;
  if (timer == 1)
    { timerctrl = timerctrl & !0x04;
      timermask = timermask & !0x01;
    }
  else
    { timerctrl = timerctrl & !0x08;
      timermask = timermask & !0x02;
    }
  outportb (gus_select, SPC_TIMER_CTRL);
  outportb (gus_hidata, timerctrl);
  outportb (gus_baseadx+TIMER_CONTROL_REG, 0x04);
  outportb (gus_baseadx+TIMER_DATA_REG, timermask | 0x80);
  asmSTI;
}


//*******************************************************************
char gus_pokedata(int adx, char data)
{
  outportb (gus_baseadx+SPC_SELECT_REG, SPC_DRAM_IO_ADX_LO);
  outportw (gus_baseadx+DATA_LO_REG, (adx & 0xffff) );
  outportb (gus_baseadx+SPC_SELECT_REG, SPC_DRAM_IO_ADX_HI);
  outportb (gus_baseadx+DATA_HI_REG, (adx >> 16) & 0xff);
  outportb (gus_baseadx+DRAM_IO_REG, data);
}

//*******************************************************************
void gus_setbalance (char voice, char data)
{
  outportb (gus_baseadx+VOICE_REG, voice);
  outportb (gus_select, VCE_BALANCE);
  outportb (gus_hidata, data & 0x0f);
}


//*******************************************************************
void gus_wait (int time)
{ int i;
  for (i=0;i<time;i++)
    { inportb(gus_baseadx); }
}

//*******************************************************************
// null function - do nothing
void gravis_null_function()
{
}

//*******************************************************************
void gus_disable_linein()
{ gus_mix = gus_mix | 0x01;
  outportb ( gus_baseadx, gus_mix );
}

//*******************************************************************
void gus_enable_linein()
{ gus_mix = gus_mix & 0xFE;
  outportb ( gus_baseadx, gus_mix );
}

//*******************************************************************
void gus_disable_micin()
{ gus_mix = gus_mix & 0xFB;
  outportb ( gus_baseadx, gus_mix );
}

//*******************************************************************
void gus_enable_micin()
{ gus_mix = gus_mix | 4;
  outportb ( gus_baseadx, gus_mix );
}

//*******************************************************************
void gus_disable_output()
{ gus_mix = gus_mix | 0x02;
  outportb ( gus_baseadx, gus_mix );
}

//*******************************************************************
void gus_enable_output()
{ gus_mix = gus_mix & 0xFD;
  outportb ( gus_baseadx, gus_mix );
}

//*******************************************************************
void gus_clearvoice (char v)
{
  outportb (gus_baseadx+VOICE_REG, v); gus_wait(7);

  // stop voice and volume
  outportb (gus_select, VCE_CTRL);
  outportb (gus_hidata,3); gus_wait(7); outportb (gus_hidata,3);
  outportb (gus_select, VCE_VOL_CTRL);
  outportb (gus_hidata,3); gus_wait(7); outportb (gus_hidata,3);
  gus_wait(14);

  // reset all voices
  // more resets may exist, just the ones I know to be vital are done
  outportb (gus_select, VCE_START_ADX_HI); outportw (gus_hidata, 0);
  outportb (gus_select, VCE_START_ADX_LO); outportw (gus_lodata, 0);
  outportb (gus_select, VCE_END_ADX_HI); outportw (gus_hidata, 0);
  outportb (gus_select, VCE_END_ADX_LO); outportw (gus_lodata, 0);
  outportb (gus_select, VCE_CURR_ADX_HI); outportw (gus_hidata, 0);
  outportb (gus_select, VCE_CURR_ADX_LO); outportw (gus_lodata, 0);
  outportb (gus_select, VCE_VOL_RAMP_RATE); outportb (gus_hidata, 1);
  outportb (gus_select, VCE_VOL_RAMP_START); outportb (gus_hidata, 0x10);
  outportb (gus_select, VCE_VOL_RAMP_END); outportb (gus_hidata, 0xE0);
}

//*******************************************************************
void gus_resethardware(void)
{
  // clear memory positions 0&1 to avoid some clicks
  char b;
  gus_pokedata (0,0); gus_pokedata (1,0);

  outportb (gus_select, SPC_RESET); outportb (gus_hidata,0); gus_wait (20);
  outportb (gus_select, SPC_RESET); outportb (gus_hidata,1); gus_wait (20);

  outportb (gus_baseadx+MIDI_CONTROL, 3); gus_wait (20);
  outportb (gus_baseadx+MIDI_CONTROL, 0); gus_wait (20);

  outportb (gus_select, SPC_DMA_CTRL); outportb (gus_hidata,0); gus_wait(20);
  outportb (gus_select, SPC_TIMER_CTRL); outportb (gus_hidata,0); gus_wait(20);
  outportb (gus_select, SPC_SAMPLE_CTRL); outportb (gus_hidata,0); gus_wait(20);

  outportb (gus_select, VCE_ACT_VOICES); outportb (gus_hidata, (gus_maxvoices-1)|0xc0); gus_wait(20);

  outportb (gus_select, SPC_DMA_CTRL); inportb(gus_hidata); gus_wait(20);
  outportb (gus_select, SPC_SAMPLE_CTRL); inportb (gus_hidata); gus_wait(20);
  outportb (gus_select, VCE_IRQ_STATUS|0x80); inportb (gus_hidata); gus_wait(20);

  for (b=0;b<31;b++)
    { gus_clearvoice(b); }

  outportb (gus_select, SPC_DMA_CTRL); inportb(gus_hidata); gus_wait(20);
  outportb (gus_select, SPC_SAMPLE_CTRL); inportb (gus_hidata); gus_wait(20);
  outportb (gus_select, VCE_IRQ_STATUS|0x80); inportb (gus_hidata); gus_wait(20);

  outportb (gus_select, SPC_RESET); outportb (gus_hidata, 7); gus_wait(20);
}

//*******************************************************************
int gusreset_probecard(void)
{
  outportb (gus_select, SPC_RESET); outportb (gus_hidata,0); gus_wait(20);
  outportb (gus_select, SPC_RESET); outportb (gus_hidata,1); gus_wait(20);

  gus_pokedata (0,0x55);
  gus_pokedata (1,0xaa);
  if (gus_peekdata(0) == 0x55)
    { return(0); }
  else
    { return(1); }
}

//*******************************************************************
void gusreset_setinterface(void)
{
  unsigned char gf1irq,midiirq,dramdma,adcdma,irqc,dmac;
  gus_mix = 0x0B;
  gf1irq = GUS_irqlatches[gus_irq];
  midiirq = GUS_irqlatches[gus_midiirq] << 3;
  dramdma = 0; adcdma = 0; dmac = 0; irqc = 0;
  if (gus_dma != 0) { dramdma = GUS_dmalatches[gus_dma]; }
  if (gus_adcdma != 0) { adcdma = GUS_dmalatches[gus_adcdma] << 3; }
  irqc = irqc | gf1irq;
  if ( (gus_irq = gus_midiirq) && (gus_midiirq != 0) )
    { irqc = irqc | 0x40; } else { irqc = irqc | midiirq; }
  dmac = dmac | dramdma;
  if ( (gus_dma = gus_adcdma) && (gus_adcdma != 0) )
    { dmac = dmac | 0x40; } else { dmac = dmac | adcdma; }

  // set up for digital ASIC
  outportb (gus_baseadx+0x0f, 0x05);
  outportb (gus_baseadx+MIX_CTRL_REG, gus_mix);
  outportb (gus_baseadx+IRQ_CTRL_REG, 0);
  outportb (gus_baseadx+0x0f, 0);

  // first do dma control reg
  outportb (gus_baseadx+MIX_CTRL_REG, gus_mix);
  outportb (gus_baseadx+IRQ_CTRL_REG, dmac | 0x80);

  // irq ctrl reg
  outportb (gus_baseadx+MIX_CTRL_REG, gus_mix | 0x40);
  outportb (gus_baseadx+IRQ_CTRL_REG, irqc);

  // redo
  outportb (gus_baseadx+MIX_CTRL_REG, gus_mix);
  outportb (gus_baseadx+IRQ_CTRL_REG, dmac | 0x80);
  outportb (gus_baseadx+MIX_CTRL_REG, gus_mix | 0x40);
  outportb (gus_baseadx+IRQ_CTRL_REG, irqc);

  // irqc, enable irq - to lock out writes to irq/dma regs
  outportb (gus_baseadx+VOICE_REG, 0);

  // enable output+irq, dis linein&micin
  gus_mix = gus_mix | 0x09;
  outportb (gus_baseadx+MIX_CTRL_REG, gus_mix);

  outportb (gus_baseadx+VOICE_REG, 0);
}

//*******************************************************************
void gusreset_setirqhandlers()
{ char b;

  girq_dma = gravis_null_function;
  girq_miditx = gravis_null_function;
  girq_midirx = gravis_null_function;
  girq_timer1 = gravis_null_function;
  girq_timer2 = gravis_null_function;
  girq_wave = gravis_null_function;
  girq_envelope = gravis_null_function;

  b = gus_irq;
  if (b != 0)
    { if (b > 7) { b = b + 0x68; } else { b = b + 8; }
      gus_intvect = b;

      _go32_dpmi_get_protected_mode_interrupt_vector(gus_intvect, &GUS_oldint);
      info.pm_offset=(unsigned long int)GRAVIS_INTERRUPT;
      info.pm_selector=_my_cs();
      _go32_dpmi_allocate_iret_wrapper(&info);
      _go32_dpmi_set_protected_mode_interrupt_vector(gus_intvect, &info);
    }

  if ((gus_midiirq != 0) && (gus_midiirq != gus_irq))
    { b = gus_midiirq;
      if (b > 7) { b = b + 0x68; } else { b = b + 0x08; }
      gus_midiintvect = b;

      _go32_dpmi_get_protected_mode_interrupt_vector(gus_midiintvect, &GUS_oldmidiint);
      info.pm_offset=(unsigned long int)GRAVIS_MIDI_INTERRUPT;
      info.pm_selector=_my_cs();
      _go32_dpmi_allocate_iret_wrapper(&info);
      _go32_dpmi_set_protected_mode_interrupt_vector(gus_midiintvect, &info);
    }
}

//*******************************************************************
int gusreset_opencard (void)
{
  if (gusreset_probecard() != 0)
    { return(1); }
  gus_disable_linein();
  gus_disable_micin();
  gus_disable_output();
  gus_resethardware();
  return (0);
}

//*******************************************************************
void gusreset_setirqs()
{
  outportb (0x20, 0x20); outportb (0xa0, 0x20);

  gus_intctrl1 = gus_oldintctrl1 = inportb (0x21);
  gus_intctrl2 = gus_oldintctrl2 = inportb (0xa1);

  if (gus_irq != 0)
    { if (gus_irq > 7)
        { gus_intctrl2 = gus_intctrl2 & !(GUS_irqmasks[gus_irq-8]); }
      else
        { gus_intctrl1 = gus_intctrl1 & !(GUS_irqmasks[gus_irq]); }
    }
  if (gus_midiirq != 0)
    { if (gus_midiirq > 7)
        { gus_intctrl2 = gus_intctrl2 & !(GUS_irqmasks[gus_midiirq-8]); }
      else
        { gus_intctrl1 = gus_intctrl1 & !(GUS_irqmasks[gus_midiirq]); }
    }

  if ( (gus_irq > 7) || (gus_midiirq > 7) )
    { gus_intctrl1 = gus_intctrl1 & !(GUS_irqmasks[2]); }

  outportb (0x21, gus_intctrl1);
  outportb (0xa1, gus_intctrl2);

  // wait to handle possible pending irqs
  gus_wait (500);
}

//*******************************************************************
int gus_reset (char avoices, short ioadx, char irq, char midiirq, char dma1, char dma2)
{ char b;
  gus_divisor = GUS_divisortable[avoices-14];
  gus_maxvoices = avoices;

  gus_baseadx = ioadx;
  gus_irq = irq;
  gus_midiirq = midiirq;
  gus_dma = dma1;
  gus_adcdma = dma2;

  gus_select = gus_baseadx + SPC_SELECT_REG;
  gus_lodata = gus_baseadx + DATA_LO_REG;
  gus_hidata = gus_baseadx + DATA_HI_REG;

  if (gusreset_probecard() != 0) { return(3); }
  if (gusreset_opencard() != 0) { return(4); }

  gusreset_setinterface(); gus_wait(50);
  gusreset_setirqhandlers(); gus_wait(50);
  gusreset_setirqs(); gus_wait(50);
  gus_enable_output(); gus_wait(50);

  inportb (gus_baseadx+MIDI_DATA_REG);
  outportb (gus_select, VCE_IRQ_STATUS | 0x80);
  b = inportb (gus_hidata) & 0x1f;
  outportb (gus_baseadx+VOICE_REG, b);

  gus_enable_output ();

  return (0);
}

//*******************************************************************
void gus_close(void)
{ char b;
  gus_stoptimer(1);
  gus_stoptimer(2);
  for (b=0;b<31;b++)
    { outportb (gus_baseadx+VOICE_REG, b);
      outportb (gus_select, VCE_CTRL); outportb (gus_hidata,3); }

  outportb (0x21, gus_oldintctrl1);
  outportb (0xa1, gus_oldintctrl2);

  gus_maxvoices = 14;
  gus_resethardware();

  outportb (gus_select,SPC_DMA_CTRL); outportb (gus_hidata,0); gus_wait(20);
  outportb (gus_select,SPC_TIMER_CTRL); outportb (gus_hidata,0); gus_wait(20);
  outportb (gus_select,SPC_SAMPLE_CTRL); outportb (gus_hidata,0); gus_wait(20);

  // clear voice ints
  inportb (gus_baseadx+IRQ_STATUS_REG); gus_wait (20);
  outportb (gus_select, SPC_DMA_CTRL); inportb (gus_hidata); gus_wait(10);
  outportb (gus_select, SPC_SAMPLE_CTRL); inportb (gus_hidata); gus_wait(10);
  outportb (gus_select, VCE_IRQ_STATUS | 0x80); inportb (gus_hidata); gus_wait(10);

  if (gus_irq != 0)
    { _go32_dpmi_set_protected_mode_interrupt_vector(gus_intvect, &GUS_oldint); }
  if ((gus_midiirq != 0) && (gus_irq != gus_midiirq))
    { _go32_dpmi_set_protected_mode_interrupt_vector(gus_midiintvect, &GUS_oldmidiint); }

  outportb (gus_select,SPC_DMA_CTRL); outportb (gus_hidata,0); gus_wait(20);
  outportb (gus_select,SPC_TIMER_CTRL); outportb (gus_hidata,0); gus_wait(20);
  outportb (gus_select,SPC_SAMPLE_CTRL); outportb (gus_hidata,0); gus_wait(20);
  inportb (gus_baseadx+IRQ_STATUS_REG); gus_wait (20);
  outportb (gus_select, SPC_DMA_CTRL); inportb (gus_hidata); gus_wait(10);
  outportb (gus_select, SPC_SAMPLE_CTRL); inportb (gus_hidata); gus_wait(10);
  outportb (gus_select, VCE_IRQ_STATUS | 0x80); inportb (gus_hidata); gus_wait(10);

}

//*******************************************************************

void gus_primevoice (char v, int beg, int loop, int end, char mode)
{
  char vm;
  outportb (gus_baseadx+VOICE_REG, v);

  outportb (gus_select, VCE_VOL_CTRL | 80);

    // rollover
  if (!(mode & 0x04))
    { vm = inportb (gus_hidata) & !0x04; }
  else
    { vm = inportb (gus_hidata) | 0x04; }

  outportb (gus_select, VCE_VOL_CTRL);
  outportb (gus_hidata, vm); gus_wait(7); outportb (gus_hidata,vm);

  outportb (gus_select, VCE_CURR_ADX_HI);
  outportw (gus_lodata, gus_adx_high(beg));
  outportb (gus_select, VCE_CURR_ADX_LO);
  outportw (gus_lodata, gus_adx_low(beg));

  outportb (gus_select, VCE_START_ADX_HI);
  outportw (gus_lodata, gus_adx_high(loop));
  outportb (gus_select, VCE_START_ADX_LO);
  outportw (gus_lodata, gus_adx_low(loop));

  outportb (gus_select, VCE_END_ADX_HI);
  outportw (gus_lodata, gus_adx_high(end));
  outportb (gus_select, VCE_END_ADX_LO);
  outportw (gus_lodata, gus_adx_low(end));

}

//*******************************************************************
void gus_startvoice (char v, char m)
{
  outportb (gus_baseadx+VOICE_REG, v);
  outportb (gus_select,VCE_CTRL);
    // bits: 08:loop, 10:birid-loop, 20:irq (04:16bit data)
  outportb (gus_hidata, m & 0x38);
  gus_wait (7);
  outportb (gus_hidata, m & 0x38);
}

//*******************************************************************
void gus_stopvoice (char v)
{ unsigned char d;
  asmCLI;
  outportb (gus_baseadx+VOICE_REG,v);
  outportb (gus_select, VCE_VOL_CTRL | 0x80);
  d = inportb(gus_hidata) & !0x80;
  outportb (gus_hidata, d); gus_wait (7); outportb (gus_hidata,d);

  outportb (gus_select, VCE_CTRL | 0x80);
  d = (inportb (gus_hidata) & !0x20) | 3;
  outportb (gus_select,VCE_CTRL);
  outportb (gus_hidata, d); gus_wait (7); outportb (gus_hidata,d);

  outportb (gus_select, VCE_CURR_ADX_HI);
  outportw (gus_lodata, 0);
  outportb (gus_select, VCE_CURR_ADX_LO);
  outportw (gus_lodata, 0);

  asmSTI;
}

//*******************************************************************
void gus_setfrequency (char v, int freq)
{ int fc;
  asmCLI;
  fc = ( (((freq << 9)) + (gus_divisor>>1)) / gus_divisor) << 1;

  outportb (gus_baseadx+VOICE_REG, v);
  outportb (gus_select, VCE_FREQ_CONTROL);
  outportw (gus_lodata, fc);
  asmSTI;
}

//*******************************************************************
void gus_playvoice (char v, int beg, int loop, int end, char mode, int freq)
{ asmCLI;
  gus_setfrequency (v,freq);
  gus_primevoice (v,beg,loop,end,mode);
  gus_startvoice (v,mode);
  asmSTI;
}

//*******************************************************************
void gus_setvolume (char v, char vol)
{ char linvol;
  short gvol;
  asmCLI;
  outportb (gus_baseadx+VOICE_REG,v);

  GUSchanvol[v] = vol;
  linvol = (vol * gus_volume) >> 6;

  gvol = GUS_linearvolume[linvol] << 4;

  outportb (gus_select, VCE_VOLUME);
  outportw (gus_lodata, gvol);

  asmSTI;
}

//*******************************************************************
void gus_setglobvol (char vol)
{ char c;
  short linvol,gvol;
  asmCLI;

  gus_volume = vol;
  for (c = 0; c<32; c++)
    { linvol = (GUSchanvol[c] * gus_volume) >> 6;
      gvol = GUS_linearvolume[linvol] << 4;
      outportb (gus_baseadx+VOICE_REG,c);
      outportb (gus_select, VCE_VOLUME);
      outportw (gus_lodata, gvol);
    }
  asmSTI;
}


//*******************************************************************
void gus_set_address (char v, int adx)
{ asmCLI;
  outportb (gus_baseadx+VOICE_REG, v);
  outportb (gus_select, VCE_CURR_ADX_HI);
  outportw (gus_lodata, gus_adx_high(adx));
  outportb (gus_select, VCE_CURR_ADX_LO);
  outportw (gus_lodata, gus_adx_low(adx));
  gus_wait(7);
  outportb (gus_select, VCE_CURR_ADX_HI);
  outportw (gus_lodata, gus_adx_high(adx));
  outportb (gus_select, VCE_CURR_ADX_LO);
  outportw (gus_lodata, gus_adx_low(adx));
  asmSTI;
}

/*
void timer2(void)
{ printf ("x");
}


//*******************************************************************
int main (void)
{
  gus_reset (14, 0x220, 5,5, 1,1);

  gus_volume = 60;
  gus_enable_output();
  girq_timer2 = timer2;

  gus_starttimer (2, 100);

  gus_playvoice (1, 256, 512, 10000, 8, 10000);
  gus_setvolume (0,60);

  while (!kbhit())
    { printf ("%d", t);
    }
  getch ();
  gus_close ();
  return(0);
} */
